// Copyright (c) [Year] [name of copyright holder]
// [Software Name] is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2. 
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2 
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
// See the Mulan PSL v2 for more details.

/*!
 * \autor captainpeng
 * \date 2019-1-15
 * \update 2019-1-15
 * \version 1.0
 * \copyright
 */

#ifndef MY_TYPEINFO_HPP
#define MY_TYPEINFO_HPP

#include<type_traits>
#include"typeDecl.hpp"
#include"typeRemove.hpp"

namespace my {

#define TYPEINFO(TYPE) #TYPE,
    const char * type_name[] = {
#include"typeInfo.def"
    };
#undef TYPEINFO
    const int type_name_size = sizeof(type_name)/sizeof(const char *);
    
    template<typename T, typename ... TYPES>
    struct typeInfoTuple : typeInfoTuple<TYPES ...>{
        using curType = T;
        using baseType = typeInfoTuple<TYPES ...>;
        static const int index = baseType::index - 1;

        baseType * base(){ return static_cast<baseType *>(this); }
        const baseType * base() const { return static_cast<const baseType *>(this); }
        
    };

#define TYPEINFO(TYPE) TYPE,
    using defTuple = typeInfoTuple<
#include"typeInfo.def"
        my::typeNil>;

    template<>
    struct typeInfoTuple<typeNil>{
        using curType = typeNil;
        static const int index = type_name_size;
    };
    

    template<typename T, typename TUPLE>
    struct typeNameT{
        // 没找 T 的时候执行的 cstr
        struct FALSE{
            static inline const char * cstr(){
                return typeNameT<T, typename TUPLE::baseType>::cstr();
            }
        };

        // 找到 T 的时候执行的 cstr
        struct TRUE{
            static inline const char * cstr(){
                return type_name[TUPLE::index];
            }
        };
        
        static inline const char * cstr(){
            return std::conditional<
                std::is_same<T, typename TUPLE::curType>::value,
                TRUE,FALSE
                >::type::cstr();
        }
    };

    template<typename T>
    struct typeNameT<T, typeInfoTuple<typeNil>>{
        static inline const char * cstr(){
            return "unkonw_type_name";
        }
    };
        
    template<typename T>
    using typeName = typeNameT<T, defTuple>;

    // 类型信息集合体
    // 非常简单的封装而已
    template<typename T>
    class typeInfoT{
    public:
        using type = T;
        using constType = const type;
        using ref = type &;
        using constRef = const type &;
        using ptr = type *;
        using constPtr = const type *;
        using ptrConst = type * const;
        using constPtrConst = const type * const;
        using move = type &&;

    };

    template<>
    class typeInfoT<void>{
    public:
        using type = void;
        using ptr = void *;
    };

    template<>
    class typeInfoT<std::nullptr_t>{
    public:
        using type = std::nullptr_t;
    };

    template<typename T>
    using typeInfo = typeInfoT<rm_all_refcv_t<T>>;
    
    //帮助类型
    template<typename T>
    using info_type_t = typename typeInfo<T>::type;

    template<typename T>
    using info_constType_t = typename typeInfo<T>::constType;
    
    template<typename T>
    using info_ref_t = typename typeInfo<T>::ref;

    template<typename T>
    using info_constRef_t = typename typeInfo<T>::constRef;

    template<typename T>
    using info_ptr_t = typename typeInfo<T>::ptr;

    template<typename T>
    using info_constPtr_t = typename typeInfo<T>::constPtr;

    template<typename T>
    using info_ptrConst_t = typename typeInfo<T>::ptrConst;

    template<typename T>
    using info_constPtrConst_t = typename typeInfo<T>::constPtrConst;

    template<typename T>
    using info_move_t = typename typeInfo<T>::move;

    // 帮助类型缩写
    template<typename T>
    using type = typename typeInfo<T>::type;

    template<typename T>
    using constt = typename typeInfo<T>::constType;
    
    template<typename T>
    using ref = typename typeInfo<T>::ref;

    template<typename T>
    using cref = typename typeInfo<T>::constRef;

    template<typename T>
    using ptr = typename typeInfo<T>::ptr;

    template<typename T>
    using cptr = typename typeInfo<T>::constPtr;

    template<typename T>
    using ptrc = typename typeInfo<T>::ptrConst;

    template<typename T>
    using cptrc = typename typeInfo<T>::constPtrConst;

    template<typename T>
    using movet = typename typeInfo<T>::move;

            
}

#endif
